package com.cauli.manage.role.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cauli.manage.menu.model.entity.RoleMenu;
import com.cauli.manage.menu.service.RoleMenuService;
import com.cauli.manage.role.mapper.RoleMapper;
import com.cauli.manage.role.model.dto.AllocatedRoleQuery;
import com.cauli.manage.role.model.dto.RoleAddDTO;
import com.cauli.manage.role.model.vo.RoleVO;
import com.cauli.manage.role.model.dto.RoleQueryDTO;
import com.cauli.manage.role.model.dto.RoleUpdateDTO;
import com.cauli.manage.role.model.dto.RoleUpdateStatusDTO;
import com.cauli.manage.role.model.dto.UnallocatedRoleQuery;
import com.cauli.manage.role.model.entity.Role;
import com.cauli.manage.role.service.RoleService;
import com.cauli.manage.user.model.dto.UserVO;
import com.cauli.manage.user.model.entity.User;
import com.cauli.manage.user.service.UserService;
import com.cauli.model.page.PageDTO;
import com.cauli.utils.exception.MyException;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;

/**
 * @author Cauli
 * @date 2022-12-13 15:16:02
 * @description 角色 服务实现类
 */
@Service
public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements RoleService {
    @Lazy
    @Autowired
    private UserService userService;

    @Autowired
    private RoleMenuService roleMenuService;

    @Override
    public Page<Role> getRolePage(RoleQueryDTO roleQueryDTO) {
        return baseMapper.selectPage(roleQueryDTO.toPage(), roleQueryDTO.toLambdaQueryWrapper());
    }

    @Override
    public RoleVO getRoleById(Long roleId) {
        Role role = baseMapper.selectById(roleId);
        RoleVO roleVO = new RoleVO();
        if (role != null) {
            BeanUtils.copyProperties(role, roleVO);
        }
        return roleVO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addRole(RoleAddDTO roleAddDTO) {
        Role role = new Role();
        BeanUtil.copyProperties(roleAddDTO, role);
        if (role.getRoleName() == null) {
            throw new MyException("角色名为空");
        }
        // 检测角色名是否被占用
        this.checkIsDuplicated(roleAddDTO.getRoleName());
        baseMapper.insert(role);
        // 添加角色对应的菜单列表
        this.setMenuByRole(roleAddDTO.getMenuIds(), role.getRoleId());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateRole(RoleUpdateDTO roleUpdateDTO) {
        // 根据角色ID获取角色信息
        Role role = baseMapper.selectById(roleUpdateDTO.getRoleId());
        BeanUtil.copyProperties(roleUpdateDTO, role);
        if (role == null) {
            throw new MyException("角色不存在");
        }
        // 修改角色对应的菜单列表
        this.setMenuByRole(roleUpdateDTO.getMenuIds(), role.getRoleId());
        // 检测角色名是否重复
        if (StrUtil.isEmpty(roleUpdateDTO.getRoleName()) && !role.getRoleName().equals(roleUpdateDTO.getRoleName())) {
            this.checkIsDuplicated(roleUpdateDTO.getRoleName());
        }
        // 更新角色
        baseMapper.updateById(role);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void changeRoleStatus(RoleUpdateStatusDTO roleUpdateStatusDTO) {
        // 根据角色ID获取角色信息
        Role role = baseMapper.selectById(roleUpdateStatusDTO.getRoleId());
        if (role == null) {
            throw new MyException("角色不存在");
        }
        BeanUtil.copyProperties(roleUpdateStatusDTO, role);
        baseMapper.updateById(role);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteRole(List<Long> roleIds) {
        for (Long roleId : roleIds) {
            // 根据角色ID获取角色信息
            Role role = baseMapper.selectById(roleId);
            AllocatedRoleQuery query = new AllocatedRoleQuery();
            query.setRoleId(roleId);
            Page<User> page = baseMapper.getUserListByRole(query.toPage(), query.toQueryWrapper());
            List<UserVO> authorizedUserList = this.getAuthorizedUserList(page);
            if (!CollectionUtil.isEmpty(authorizedUserList)) {
                throw new MyException("角色已分配给用户，请先取消分配，再删除角色");
            }
            baseMapper.deleteById(role);
        }
    }

    @Override
    public PageDTO<UserVO> getAllocatedUserList(AllocatedRoleQuery query) {
        Page<User> page = baseMapper.getUserListByRole(query.toPage(), query.toQueryWrapper());
        List<UserVO> authorizedUserList = this.getAuthorizedUserList(page);
        return new PageDTO<>(authorizedUserList, page.getTotal());
    }

    @Override
    public PageDTO<UserVO> getUnallocatedUserList(UnallocatedRoleQuery query) {
        Page<User> page = baseMapper.getUserListByRole(query.toPage(), query.toQueryWrapper());
        List<UserVO> authorizedUserList = this.getAuthorizedUserList(page);
        return new PageDTO<>(authorizedUserList, page.getTotal());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addRoleOfUserByBulk(Long roleId, List<Long> userIds) {
        if (CollUtil.isEmpty(userIds)) {
            return;
        }
        Role role = baseMapper.selectById(roleId);
        if (role == null) {
            throw new MyException("找不到ID为 [" + roleId + "] 的角色");
        }
        if (role.getStatus().equals(0)) {
            throw new MyException("角色 [" + role.getRoleName() + "] 已禁用，无法分配给用户");
        }

        for (Long userId : userIds) {
            User user = userService.getById(userId);
            if (user == null) {
                throw new MyException("找不到ID为 [" + userId + "] 的用户");
            }
            user.setRoleId(roleId);
            userService.updateById(user);
        }
    }

    @Override
    public void deleteRoleOfUserByBulk(List<Long> userIds) {
        if (CollUtil.isEmpty(userIds)) {
            return;
        }
        for (Long userId : userIds) {
            LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.set(User::getRoleId, null)
                    .eq(User::getUserId, userId);
            userService.update(updateWrapper);
        }
    }

    /**
     * 检测角色名是否被占用
     *
     * @param roleName
     */
    private void checkIsDuplicated(String roleName) {
        if (baseMapper.exists(new LambdaQueryWrapper<Role>().eq(Role::getRoleName, roleName))) {
            throw new MyException("该角色名已被占用");
        }
    }

    /**
     * 设置角色对应的菜单列表
     *
     * @param menuIds
     * @param roleId
     */
    private void setMenuByRole(List<Long> menuIds, Long roleId) {
        // 先删除roleId下所有的菜单
        LambdaUpdateWrapper<RoleMenu> roleLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        roleLambdaUpdateWrapper.eq(RoleMenu::getRoleId, roleId);
        roleMenuService.remove(roleLambdaUpdateWrapper);

        // 再将新的菜单添加
        List<RoleMenu> roleMenuList = new ArrayList<>();
        if (menuIds != null) {
            for (Long menuId : menuIds) {
                RoleMenu roleMenu = new RoleMenu();
                roleMenu.setRoleId(roleId);
                roleMenu.setMenuId(menuId);
                roleMenuList.add(roleMenu);
            }
            roleMenuService.saveBatch(roleMenuList);
        }
    }

    /**
     * 根据 条件 查询 该角色授权或未授权的用户
     *
     * @param page
     * @return
     */
    private List<UserVO> getAuthorizedUserList(Page<?> page) {
        List<UserVO> userVOList = new ArrayList<>();
        page.getRecords().forEach(user -> {
            UserVO userVO = new UserVO();
            BeanUtil.copyProperties(user, userVO);
            userVOList.add(userVO);
        });
        return userVOList;
    }
}




